iT邦幫忙

0

Redis 學習筆記(6)-商用環境的 Redis

  • 分享至 

  • xImage
  •  

Redis 學習筆記(6)-商用環境的 Redis

本文是有關 Redis 的學習筆記的一部分,相關目錄請參考 Redis 學習筆記(1)-簡介

上一篇我們說明如何在Spring Boot內連結到Redis伺服器內,當時是使用單機模式的Redis,但是在企業內使用系統服務時,會非常注重服務的高可用性,帶有單點失效瑕疵的單機模式是不被接受的。在Redis架構中除了單機模式外還有兩種,分別是叢集(cluster)和主從複製(master-replica),下面我們分別說明。

1. 關於Redis叢集模式

Redis叢集模式是將資料分散在不同的主節點上,客戶端連上任一主節點上就可以讀寫任何鍵,而計算目的鍵是位於那一個主節點上則是由Redis叢集來計算。其示意圖如下:
https://ithelp.ithome.com.tw/upload/images/20220615/20149259jqETLYrJOl.png?o=2022-06-15_01.png

Redis叢集的目標應該是作資料的水平擴容,資料是分散在不同主節點上,主節點再將資料複製到從節點。若是主節點遺失,則會將主節點對應的從節點提昇為主節點。下圖為Redis1遺失後Redis4被提昇為主節點的示意圖。
https://ithelp.ithome.com.tw/upload/images/20220615/201492593679TNz3h4.png?o=2022-06-15_02.png

2. 關於Redis主從複製模式

Redis主從複製模式是在一群節點中,其中一個是主節點,其他為從節點,客戶端連線到主節點,執行讀寫操作,而資料異動則持續由主節點複製到從節點。其示意圖如下:
https://ithelp.ithome.com.tw/upload/images/20220615/20149259zpNfVBUyYQ.png?o=2022-06-15_03.png

上面的描述會衍生二個問題,一是客戶端如何知道那一個Redis伺服器是主節點,二是若主節點遺失後,如何產出新的主節點讓服務繼續。這二個問題是透過Sentinel來解決,Sentinel也是以一個叢集存在,Sentinel叢集內的節點會監看所有的Redis主從節點,若Redis主節點遺失,Sentinel叢集內的節點會選出下一個主節點,被選出的從節點就提昇為主節點,同時客戶端也可以詢問Sentinel叢集而得知最新的主節點。其示意圖如下:
https://ithelp.ithome.com.tw/upload/images/20220615/20149259q0Xm71pC5w.png?o=2022-06-15_04.png

下面這張圖則表示在Redis1遺失後,Sentinel叢集確認後並協助選出Redis2為主節點,Redis2提昇為主節點,客戶端透過詢問Sentinel叢集得知最新主節點後,將客戶端的連線轉到Redis2上。
https://ithelp.ithome.com.tw/upload/images/20220615/2014925919YGrL8BSP.png?o=2022-06-15_05.png

在已知Redis叢集模式和Redis主從複製模式這二個模式都具備高可用性,那應該使用那一種模式呢?這必須依實際情況來判斷,Redis叢集雖然有較大的資料容量,但其也有其他缺點,如在一個指令含多鍵操作時,只有在這些鍵全位於同一個Redis下才會有操作原子性。

對我而言,Redis主從複製模式比較符合我的需求,所以我只針對這個模式作測試。

3. Redis主從複製環境架構

在決定採用Redis主從複製模式後,那Redis和Sentinel該部署在何種環境上?可以選擇部置在Linux VM主機上,也可以部署在Docker或Docker Swarm上。我的決定是使用Docker Swarm,因為使用容器技術可以省去軟體安裝和函式庫環境相容性的問題,而使用Swarm則可以由一架主機上下指令來帶起其他Docker主機上的服務。

下圖是我的測試架構,準備三架 VM 主機,每架主機上皆啟動 Docker 環境,在每個 Docker 環境中各啟動一個 Redis 和 Sentinel。 啟動的 Redis (Master/Replica) 皆是 Swarm 內的服務,且該服務必須固定在指定的 Swarm 節點上,不可任意遷移。
https://ithelp.ithome.com.tw/upload/images/20220615/20149259cEPbudjhFG.png?o=2022-05-24_01.png

瞭解測試環境架構後,接下來先建立Docker Swarm環境。

4. 建立Docker Swarm環境

我們不說明如何建立VM及在其建立Docker環境,直接假設這些是已備好的資源,在這些前題上建立Docker Swarm環境。

建立Docker Swarm環境的動作分解下面項目:

  • 準備三架 VM 主機(含 Docker 環境)
  • 在三架 VM 上開啟必要的防火牆
  • 將三架 Docker 整合成一個 Swarm 環境
  • 在 Swarm 三個節點上設定識別標籤

我們先由準備VM主機開始。

4.1 準備三架 VM 主機

準備三架 VM 主機,已安裝好 Docker 環境,其相關資訊(IP/Hostname)如下:

  • cfa1 (IP:10.0.2.11/24):
    https://ithelp.ithome.com.tw/upload/images/20220615/20149259lawK9kBdaE.png?o=2022-05-24_02.png
  • cfa2 (IP:10.0.2.12/24):
    https://ithelp.ithome.com.tw/upload/images/20220615/201492598bWAlL8ott.png?o=2022-05-24_03.png
  • cfa3 (IP:10.0.2.13/24):
    https://ithelp.ithome.com.tw/upload/images/20220615/20149259tXapijGPCo.png?o=2022-05-24_04.png

在之後的說明或設定中,會直接使用上面的主機名稱或IP。

4.2 在三架 VM 上開啟必要的防火牆

每架主機上皆必須針對之後會用到的埠號作開啟防火牆的操作,其操作指令如下:

firewall-cmd --add-service=docker-swarm --permanent
firewall-cmd --add-service=redis --permanent
# port 26379 為 Sentinel 使用
firewall-cmd --add-port=26379/tcp --permanent

firewall-cmd --reload
firewall-cmd --list-all

4.3 將三個 Docker 節點整合成一個 Swarm 環境

接下來我們要將三個獨立的Docker節點整合成一個Swarm叢集,其指令如下:

  • 若原 Docker 仍屬於舊集群可用下列指令將其移除

    docker swarm leave -f
    
    
  • 在 c7a1 上初始化一個新集群

    docker swarm init --advertise-addr 10.0.2.11
    
    
  • 取得加入 Swarm master 的指令:

    docker swarm join-token manager
    
    

    指令產出類似下方結果:

    [root@c7a1 ~]# docker swarm join-token manager
    To add a manager to this swarm, run the following command:
      docker swarm join --token SWMTKN-1-1fvir6k61i4ayua1ycjc7ik7j43ap3uxe7hfkbr6pyux9l6e0c-90vlcfmhvm0sin7hjd4r5j60u 10.0.2.11:2377
    
    [root@c7a1 ~]# 
    
  • 在 c7a2,c7a3 主機上分別執行上面產生的指令

    docker swarm join --token SWMTKN-1-1fvir6k61i4ayua1ycjc7ik7j43ap3uxe7hfkbr6pyux9l6e0c-90vlcfmhvm0sin7hjd4r5j60u 10.0.2.11:2377
    
    
  • 完成後可在 c7a1,c7a2,c7a3 任一架主機執行下列指令,檢視集群成員

    docker node ls
    
    

4.4 在 Swarm 三個節點上設定識別標籤

在預備架設 Redis + Sentinel 架構中,每個 Redis 皆是固定在指定的節點上。所以需要在每個 Docker 節點上加入唯一的識別標籤。

  • 執行 Docker 節點標籤設定的方式如下:

    ## 執行標籤設定
    docker node update --label-add name=c7a1 c7a1
    docker node update --label-add name=c7a2 c7a2
    docker node update --label-add name=c7a3 c7a3
    
    
  • 檢查設定結果

    ## 檢查目前標籤設定值
    docker node ls -q | xargs docker node inspect -f '{{ .ID }} [{{ .Description.Hostname }}]: {{ .Spec.Labels }}'
    
    

5. 在 Swarm 建立 Redis和Sentinel服務

在擁有Swarm環境後,可以在其上建立Redis和Sentinel服務,雖然這二個是不同的Swarm服務,但是我們放在一起說明。

建立Redis和Sentinel的Swarm服務,分為下列幾個操作:

  • 建立 Volume
  • 建立 Redis 設定檔
  • 建立 Sentinel 設定檔
  • 建立 部署服務的 compose 檔案

5.1 建立 Volume

Redis 為確保資料在重啟容器後不遺失,就必須將資料寫到 Volume 中。其架構如下圖:
https://ithelp.ithome.com.tw/upload/images/20220615/20149259h7EaulmWvu.png?o=2022-05-25_01.png

所以必須在每個節點建立 Volume,因為 Volume 是固定在指定的節點,所以對應的 Redis 也必須啟動固定的節點(這也就是之前在每個節點建立唯一標籤的原因)。

  • 建立 Volume 指令如下,該指令必須在 c7a1/c7a2/c7a3 皆執行。

    # 清除未使用的 Volume
    docker volume prune -f
    
    # 建立 Volume
    docker volume create redis
    
    # 建立 redis & sentinel conf 使用的目錄
    mkdir -p /var/lib/docker/volumes/redis/_data/conf
    
    # 建立 redis data 使用的目錄
    mkdir -p /var/lib/docker/volumes/redis/_data/data
    
    # 參考 docker hub / redis 的 Docker file, 其執行時使用 uid 999 故要更改 volume 目錄的權限
    chown -R 999:999 /var/lib/docker/volumes/redis/_data
    
    # 檢視當前的 Volume
    docker volume ls
    
    

5.2 建立 Redis 設定檔

計劃當 Redis 容器啟動後會將 Volume redis 掛載在 Redis 容器內,所以我們可以直將 Redis 使用的設定檔直接放在 Volume redis 內。當 Redis 容器起動後就可直接讀取容器內 Volume 內的設定檔。

5.2.1 直接設定 Master Redis 的設定檔

預定先讓 c7a1 上的 Redis 為 Master,而 Master 的設定檔和 Replica 是有小部份不同。

  • 在 c7a1 的節點上執行設定檔配置的指令

    ## 直接將 Master Redis 設定檔寫到 Volume redis 內的空間
    cat << EOF | tee /var/lib/docker/volumes/redis/_data/conf/redis.conf
    port 6379
    bind 0.0.0.0
    dir /redis/data/
    protected-mode yes
    requirepass mypwd
    dbfilename dump.rdb
    save 900 1
    save 300 10
    save 60 10000
    appendonly yes
    appendfsync everysec
    
    masterauth mypwd
    replica-priority 100
    EOF
    
    
  • 重設該設定檔的 uid:gid,讓 redis容器可以存取。

    # 參考 docker hub / redis 的 Docker file, 其執行時使用 uid 999 故要更改 volume 目錄的權限
    chown -R 999:999 /var/lib/docker/volumes/redis/_data
    
    

5.2.2 直接設定 Replica Redis 的設定檔

預定 c7a2 和 c7a3 上的 Redis 為 Replica,而 Replica 的設定檔比 Master 多了一項 slaveof設定。

  • 在 c7a2/c7a3 的節點上執行設定檔配置的指令

    ## 設定 Master 為 c7a1
    MASTER_IP=10.0.2.11
    
    ## 建立 redis (slave1) 設定檔
    cat << EOF | tee /var/lib/docker/volumes/redis/_data/conf/redis.conf
    port 6379
    bind 0.0.0.0
    dir /redis/data/
    protected-mode yes
    requirepass mypwd
    dbfilename dump.rdb
    save 900 1
    save 300 10
    save 60 10000
    appendonly yes
    appendfsync everysec
    slaveof ${MASTER_IP} 6379
    masterauth mypwd
    replica-priority 101
    EOF
    
    
  • 重設該設定檔的 uid:gid,讓 redis容器可以存取。

    # 參考 docker hub / redis 的 Docker file, 其執行時使用 uid 999 故要更改 volume 目錄的權限
    chown -R 999:999 /var/lib/docker/volumes/redis/_data
    
    

5.3 建立 Sentinel 設定檔

計劃 Sentinel 和 Redis 一樣,Sentinel 啟動後就會將 Volume redis 掛載在 Sentinel 容器。所以 Sentinel 的設定檔也可以直接放在 Volume redis 內。當 Sentinel 容器起動後就可直接讀取容器內 Volume 內的設定檔。

對 Sentinel 而言,沒有主從之分,所以在 c7a1/c7a2/c7a3 上的設定檔應該是完全相同。

  • 在 c7a1/c7a2/c7a3 的節點上執行設定檔配置的指令,如下:

    ## 設定 Master 為 c7a1
    MASTER_IP=10.0.2.11
    
    ## 建立 sentinel 設定檔
    cat << EOF | tee /var/lib/docker/volumes/redis/_data/conf/sentinel.conf
    port 26379
    sentinel resolve-hostnames yes
    sentinel monitor mymaster ${MASTER_IP} 6379 2
    sentinel down-after-milliseconds mymaster 5000
    sentinel failover-timeout mymaster 60000
    sentinel parallel-syncs mymaster 1
    sentinel auth-pass mymaster mypwd
    EOF
    
    
  • 重設該設定檔的 uid:gid,讓 redis容器可以存取。

    # 參考 docker hub / redis 的 Docker file, 其執行時使用 uid 999 故要更改 volume 目錄的權限
    chown -R 999:999 /var/lib/docker/volumes/redis/_data
    
    

5.4 建立 部署服務的 compose 檔案

在完成 Redis 和 Sentinel 的設定檔後,還須有 Swarm 部署服務用的 compose 檔案。該檔僅需放在 c7a1 主機上,計劃由 c7a1 主機來啟動 Swarm 上的服務。

計劃將 Redis 和 Sentinel 分成二個不同的 Swarm 服務,所以必須準備不同的 compose 檔案。分別為:

  • redis.yml

  • sentinel.yml

在實測過程中,發現使用 redis:7 和 redisson:3.17.3 搭配時,無法使用在 Sentinel 的環境下。觀察到的現像為 redisson 和 Sentinel 建立連線後會詢問從機資訊,其使用的指令為 sentinel slaves。但是在 redis:7 該指令已移除,改用指令 sentinel replicas,redisson:3.17.3 無法支援新指令。最後只能暫時放棄 redis:7 改用 redis:6.2.3。

在Redis官網上,有說明若使用Docker環境時,Docker網路應選用host模式。

5.4.1 建立 Redis 使用的 redis.yml

  • 在 c7a1 上建立 compose 檔案,如下:

    cd /root
    mkdir -p /root/redis_stack
    cd /root/redis_stack
    
    cat << EOF | tee /root/redis_stack/redis.yml
    version: "3.6"
    services:
      master: 
        image: "redis:6.2.3"
        command: redis-server /redis/conf/redis.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a1"
      replica1: 
        image: "redis:6.2.3"
        command: redis-server /redis/conf/redis.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a2"
      replica2: 
        image: "redis:6.2.3"
        command: redis-server /redis/conf/redis.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a3"
    volumes:
      redis:
        external: true
    networks:
      hostnet:
        external: true
        name: host
    EOF
    
    

5.4.2 建立 Sentinel 使用的 sentinel.yml

  • 在 c7a1 上建立 compose 檔案,如下:

    cd /root
    mkdir -p /root/redis_stack
    cd /root/redis_stack
    
    cat << EOF | tee /root/redis_stack/sentinel.yml
    version: "3.6"
    services:
      sent0:
        image: "redis:6.2.3"
        command: redis-sentinel /redis/conf/sentinel.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a1"
      sent1:
        image: "redis:6.2.3"
        command: redis-sentinel /redis/conf/sentinel.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a2"
      sent2:
        image: "redis:6.2.3"
        command: redis-sentinel /redis/conf/sentinel.conf
        networks:
          hostnet: {}
        volumes:
          - redis:/redis
        deploy:
          replicas: 1
          placement:
            constraints:
              - "node.labels.name==c7a3"
    volumes:
      redis:
        external: true
    networks:
      hostnet:
        external: true
        name: host
    EOF
    
    

5.5 啟動 Redis+Sentinel 服務

完成所有設定後,接下來需要瞭解的是啟動服務。啟動服務的次序為先啟動 Redis 服務,服務正常後再啟動 Sentinel 服務。

  • 啟動 Redis 服務的方式如下:

    cd /root/redis_stack
    
    ## 啟動服務將其命名為 redis
    docker stack deploy -c redis.yml redis
    
    ## 檢查 docker stack 帶起來的服務
    docker stack ls
    
    ## 檢查 docker stack 內 redis 服務的狀態
    docker stack ps redis
    
    ## 檢查 docker swarm 上的服務
    docker service ls
    
    
  • 啟動 Sentinel 服務的方式如下:

    cd /root/redis_stack
    
    ## 啟動服務將其命名為 sentinel
    docker stack deploy -c sentinel.yml sentinel
    
    ## 檢查 docker stack 帶起來的服務
    docker stack ls
    
    ## 檢查 docker stack 內 sentinel 服務的狀態
    docker stack ps sentinel
    
    

6. 驗證 Redis和Sentinel 服務

Redis+Sentinel 服務啟動後,可以作簡單的驗證。

6.1 驗證 Replication 服務

當前的 Master 為 c7a1,驗證在 c7a1 寫入資料是否可以在 c7a2 見到相同的資料。

  • 在 c7a1 執行登入 redis 並寫入一筆資料

    • 在 c7a1 進入 redis 容器並建立連線

      ## 進入 redis 容器
      docker exec -it `docker ps -q -f 'name=redis'` /bin/bash
      
      ## 進入容器後,再連線到本地的 redis
      redis-cli -a mypwd
      
      
    • 設定一筆資料,並註明是由 c7a1 設定的

      set k1 v1_from_c7a1
      
      
  • 在 c7a2 執行登入 redis 並檢查剛寫入的資料

    ## 進入 redis 容器
    docker exec -it `docker ps -q -f 'name=redis'` /bin/bash
    
    ## 進入容器後,再連線到本地的 redis
    redis-cli -a mypwd
    
    ## 檢查剛寫入的資料  
    get k1
    
    

6.2 驗證 Sentinel 服務

Sentinel 主要服務二個功能。第一個功能是回覆客戶端當前 Master 主機的位置(IP+PORT)。第二個功能是監控 Master 是否正常,若發現異常則作切換。

  • 連上 Sentinel 查找 Master 主機位置

    • 在 c7a1/c7a2/c7a3 登入 Sentinel 並詢問 Master

      ## 進入 sentinel 容器
      docker exec -it `docker ps -q -f 'name=sentinel'` /bin/bash
      
      ## 進入容器後,再連線到本地的 sentinel
      redis-cli -p 26379
      
      ## 詢問 Master 
      sentinel get-master-addr-by-name mymaster
      
      ## 上述詢問會得到 Master 的 IP + POrt
      
  • 關閉 Master 進程,確認切換發生,Replica 主機提昇為 Master 主機

    • 連上 Master(應為 c7a1),中止 Master 程序

      ## 在 Master 主機上停止 redis 進程
      docker stop `docker ps -q -f 'name=redis'`
      
      
    • 連上任一個 Sentinel 詢問 Master

      ## 進入 sentinel 容器
      docker exec -it `docker ps -q -f 'name=sentinel'` /bin/bash
      
      ## 進入容器後,再連線到本地的 sentinel
      redis-cli -p 26379
      
      ## 詢問 Master 
      sentinel get-master-addr-by-name mymaster
      
      ## 上述詢問會得到 Master 的 IP + POrt
      

7. 移除 Redis和Sentinel服務

在完成測試後,要移除所有的測試資源,其操作非常簡單,僅需下列指令操作即可:

  • 移除服務的方式如下:

    ## 移除 sentinel 服務
    docker stack rm sentinel
    
    ## 移除 redis 服務
    docker stack rm redis
    
    

8. 結論

在本篇學習中,我們介紹了Redis兩種高可用模式,一種是Redis叢集模式,另一種是Redis主從複製模式。

我們也說明了測試環境架構,和如何搭建我們的測試環境,其中包涵了Docker Swarm的搭建和在Swarm上搭建Redis和Sentinel的服務。

最後我們驗證了Redis和Sentinel服務皆符合我們的預期。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言